//**********************************************************************
//	Programmer	-	Sujal Bista
//	File Name	-	GraphicsDX.h
//	Description	:	
//					API handler for Direct X
//**********************************************************************

#ifndef _GRAPHICS_DX_H_
#define _GRAPHICS_DX_H_ 

#include "graphicsBase.h"

#include <d3d11.h>
#include <d3dx11.h>
#include <d3dcompiler.h>

class graphicsDX:public graphicsBase
{
		//form related variables
		HWND hWnd;						// handle to main window

		//dx related variables
		D3D_DRIVER_TYPE driverType;
		D3D_FEATURE_LEVEL featureLevel;
		IDXGISwapChain* pSwapChain;

		unsigned int syncInterval;	//sync interval 0=disable 1=enable

	public:

		//class to device
		class deviceD3D:public deviceBase
		{
			public:
				ID3D11Device *data;

				//constructor
				deviceD3D(void):deviceBase()
				{
					data=NULL;
				}

				//create deferred context
				bool createDeferredContext(deviceContextBase* dcData);

				//creates vertex shader
				bool createVertexShader(blobBase* shRawData,shaderVertexBase* shData);
				//creates hull shader
				bool createHullShader(blobBase* shRawData,shaderHullBase* shData);
				//creates domain shader
				bool createDomainShader(blobBase* shRawData,shaderDomainBase* shData);
				//creates geometry shader
				bool createGeometryShader(blobBase* shRawData,shaderGeometryBase* shData);
				//creates pixel shader
				bool createPixelShader(blobBase* shRawData,shaderPixelBase* shData);
				//creates compute shader
				bool createComputeShader(blobBase* shRawData,shaderComputeBase* shData);


				//creates input layout
				bool createInputLayout(inputElementDesc *inputDesc, unsigned int inputDescCount,blobBase *shData,inputLayoutBase *inLayData);
		
				//creates buffer in gpu
				bool createBuffer(bufferDesc desc, void *pSysMem, unsigned int sysMemPitch, unsigned int sysMemSlicePitch, bufferBase *bData);
				//creates 1D texture
				bool createTexture1D(texture1DDesc desc, void *pSysMem, unsigned int sysMemPitch, unsigned int sysMemSlicePitch, texture1DBase *tData);
				//creates 2D texture
				bool createTexture2D(texture2DDesc desc, void *pSysMem, unsigned int sysMemPitch, unsigned int sysMemSlicePitch, texture2DBase *tData);
				//creates 3D texture
				bool createTexture3D(texture3DDesc desc, void *pSysMem, unsigned int sysMemPitch, unsigned int sysMemSlicePitch, texture3DBase *tData);
		
				//creates shader resource view from file
				bool createShaderResourceViewFromFile(char* fileName, shaderResourceViewBase * srvData);
				//creates shader resource view. param0, param1, param2, param3 depends on the dimension type. Please refer to dx version for params
				bool createShaderResourceView(resourceBase *bData, graphicsDefinitions::DATA_FORMAT format, graphicsDefinitions::SRV_DIMENSION dimension, unsigned int param0, unsigned int param1, unsigned int param2, unsigned int param3,shaderResourceViewBase* srvData);
				//creates unordered access view. param0, param1, param2 depends on the dimension type. Please refer to dx version for params
				bool createUnorderedAccessView(resourceBase *bData, graphicsDefinitions::DATA_FORMAT format, graphicsDefinitions::UAV_DIMENSION dimension, unsigned int param0, unsigned int param1, unsigned int param2, unorderedAccessViewBase* uavData);
				
				//creates sampler state
				bool createSamplerState(graphicsDefinitions::FILTER filter,	graphicsDefinitions::TEXTURE_ADDRESS_MODE addMode[3], float mipLODBias, unsigned int maxAnisotropy, graphicsDefinitions::COMPARISON_FUNC comparisonFunc, float borderColor[4], float minLOD, float maxLOD, samplerStateBase *ssBase);
		
				//creates rasterizer state
				bool createRasterizerState(graphicsDefinitions::FILL_MODE fillMode,graphicsDefinitions::CULL_MODE cullMode,bool frontCounterClockwise,int depthBias,float depthBiasClamp,float slopeScaledDepthBias,bool depthClipEnable, bool scissorEnable, bool multisampleEnable, bool antialiasedLineEnable,	rasterizerStateBase *rsData);

				//creates a blend state
				bool createBlendState(const blendDesc *bStateDesc,blendStateBase *bStateData);

				//clears the device
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~deviceD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle immediate Contex
		class deviceContextD3D:public deviceContextBase
		{
			public:
				ID3D11DeviceContext *data;

				//constructor
				deviceContextD3D(void):deviceContextBase()
				{
					data=NULL;
				}

				//resizes the output screen
				bool resize(const unsigned int &_width,const unsigned int &_height);

				//Restore all default settings of the device context
				void clearState(void);

				//create a command list and record command
				bool finishCommandList(bool restoreDeferredContextState, commandListBase* clData);
				//queues command from command list to the device
				void executeCommandList(commandListBase* clData, bool restoreContextState);

				//sets input layout
				void setInputLayout(inputLayoutBase *inLayData);
				
				//updates sub resource
				void updateSubResource(resourceBase *destinationBuffer, unsigned int destinationSubresource, const box *destinationBox, const void *sourceData, unsigned int sourceRowPitch, unsigned int sourceDepthPitch);
				//copies one buffer to another
				void copyResource(resourceBase *dest, resourceBase *src);
				//maps the data
				void *map(resourceBase *bData, unsigned int subresource, graphicsDefinitions::MAP mapType,unsigned int &rowPitch, unsigned int &depthPitch);
				//unmap the data
				void unmap(resourceBase *bData, unsigned int subresource);

				//sets vertex buffer
				void setVertexBuffers(unsigned int startSlot,unsigned int bufferCount, bufferBase *bufferDatas,unsigned int strides,unsigned int offsets);
				//sets index buffer
				void setIndexBuffer(bufferBase *bufferData,graphicsDefinitions::DATA_FORMAT format, unsigned int offset);

				//sets primitive topology
				void setPrimitiveTopology(graphicsDefinitions::PRIMITIVE_TOPOLOGY topology);

				//Draw non-indexed, non-instanced primitives
				void draw(unsigned int vertexCount,unsigned int startVertexLocation);
				//draw indexed primitive
				void drawIndexed(unsigned int indexCount,unsigned int startIndexLocation,int baseVertexLocation);
				//Draw non-indexed, instanced primitives.
				void drawInstanced(unsigned int vertexCountPerInstance,unsigned int instanceCount,unsigned int startVertexLocation,unsigned int startInstanceLocation);
				//Draw indexed, instanced primitives.
				void drawIndexedInstanced(unsigned int indexCountPerInstance,unsigned int instanceCount,unsigned int startIndexLocation,int baseVertexLocation,unsigned int startInstanceLocation);
				//Draw instanced, GPU-generated primitives.
				void drawInstancedIndirect(bufferBase *pBufferForArgs,unsigned int alignedByteOffsetForArgs);
				//Draw indexed, instanced, GPU-generated primitives.
				void drawIndexedInstancedIndirect(bufferBase *pBufferForArgs,unsigned int alignedByteOffsetForArgs);
				
				//clear the render target
				void clearRenderTargetView(renderTargetViewBase *rTargetView, float *clearColor);

				//Clears the depth-stencil resource
				void clearDepthStencilView(depthStencilViewBase *dStencilView,unsigned int clearFlags, float depth, unsigned int stencil);

				//set constant buffer in vertex shader
				void VSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);
				//set constant buffer in hull shader
				void HSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);
				//set constant buffer in domain shader
				void DSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);
				//set constant buffer in geometry shader
				void GSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);
				//set constant buffer in pixel shader
				void PSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);
				//set constant buffer in compute shader
				void CSSetConstantBuffers(unsigned int startSlot,unsigned int numBuffers, bufferBase *constantBuffers, unsigned int uniformIndex);

				//set shader resources in vertex shader
				void VSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set shader resources in hull shader
				void HSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set shader resources in domain shader
				void DSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set shader resources in geometry shader
				void GSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set shader resources in pixel shader
				void PSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set shader resources in compute shader
				void CSSetShaderResources(unsigned int startSlot,unsigned int numViews, shaderResourceViewBase *shaderResources, unsigned int uniformIndex);
				//set unordered access view in compute shader
				void CSSetUnorderedAccessViews(unsigned int startSlot,unsigned int numUAVs, unorderedAccessViewBase *uavResources,unsigned int *initialCounts);

				//set sampler in vertex shader
				void VSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);
				//set sampler in hull shader
				void HSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);
				//set sampler in domain shader
				void DSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);
				//set sampler in geometry shader
				void GSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);
				//set sampler in pixel shader
				void PSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);
				//set sampler in compute shader
				void CSSetSamplers(unsigned int startSlot,unsigned int numSamplers, samplerStateBase *samplers);

				//set vertex shader
				void VSSetShader(shaderVertexBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//set hull shader
				void HSSetShader(shaderHullBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//set domain shader
				void DSSetShader(shaderDomainBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//set geometry shader
				void GSSetShader(shaderGeometryBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//set pixel shader
				void PSSetShader(shaderPixelBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//set compute shader
				void CSSetShader(shaderComputeBase *shader, classInstanceBase *classInstances,unsigned int numClassInstances);
				//called after all the shaders are set before drawing
				void shaderBegin(void);
				//called after all the drawing is done
				void shaderEnd(void);
				//returns uniform block index. only used in opengl
				unsigned int getUniformBlockIndex(char *name);
				//returns uniform location. only used in opengl
				unsigned int getUniformLocation(char *name);


				//sets rasterizer state
				void RSSetState(rasterizerStateBase *rsData);


				//execute direct compute command list from a thread group
				void dispatch(unsigned int threadGroupCountX, unsigned int threadGroupCountY, unsigned int threadGroupCountZ);
		


				//clears the deviceContext
				void clear(void)
				{
					if(data)
					{
						data->ClearState();
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~deviceContextD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle buffer
		class bufferD3D:public bufferBase
		{
			public:
				ID3D11Buffer *data;

				//constructor
				bufferD3D(void):bufferBase()
				{
					data=NULL;
				}

				//returns the type
				graphicsDefinitions::RESOURCE_DIMENSION getType(void)
				{
					return graphicsDefinitions::RESOURCE_DIMENSION_BUFFER;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~bufferD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle 1D texture
		class texture1DD3D:public texture1DBase
		{
			public:
				ID3D11Texture1D *data;

				//constructor
				texture1DD3D(void):texture1DBase()
				{
					data=NULL;
				}

				//returns the type
				graphicsDefinitions::RESOURCE_DIMENSION getType(void)
				{
					return graphicsDefinitions::RESOURCE_DIMENSION_TEXTURE1D;
				}

				//clears the texture1D
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~texture1DD3D()
				{
					//clears the texture1D
					clear();
				}
		};

		//class to handle 2D texture
		class texture2DD3D:public texture2DBase
		{
			public:
				ID3D11Texture2D *data;

				//constructor
				texture2DD3D(void):texture2DBase()
				{
					data=NULL;
				}

				//returns the type
				graphicsDefinitions::RESOURCE_DIMENSION getType(void)
				{
					return graphicsDefinitions::RESOURCE_DIMENSION_TEXTURE2D;
				}

				//clears the texture2D
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~texture2DD3D()
				{
					//clears the texture2D
					clear();
				}
		};

		//class to handle 3D texture
		class texture3DD3D:public texture3DBase
		{
			public:
				ID3D11Texture3D *data;

				//constructor
				texture3DD3D(void):texture3DBase()
				{
					data=NULL;
				}

				//returns the type
				graphicsDefinitions::RESOURCE_DIMENSION getType(void)
				{
					return graphicsDefinitions::RESOURCE_DIMENSION_TEXTURE3D;
				}

				//clears the texture3D
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~texture3DD3D()
				{
					//clears the texture3D
					clear();
				}
		};
		
		//class to handle raw shader data
		class blobD3D:public blobBase
		{
			public:
				ID3DBlob *data;

				//constructor
				blobD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~blobD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle input layout for vertex
		class inputLayoutD3D:public inputLayoutBase
		{
			public:
				ID3D11InputLayout *data;

				//constructor
				inputLayoutD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~inputLayoutD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//render target view
		class  renderTargetViewD3D:public renderTargetViewBase
		{
			public:
				ID3D11RenderTargetView *data;

				//constructor
				renderTargetViewD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~renderTargetViewD3D()
				{
					//clears the buffer
					clear();
				}
		};
		//depth stencil view
		class depthStencilViewD3D:public depthStencilViewBase
		{
			public:
				ID3D11DepthStencilView *data;

				//constructor
				depthStencilViewD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~depthStencilViewD3D()
				{
					//clears the buffer
					clear();
				}
		};
		//shader resource view
		class shaderResourceViewD3D:public shaderResourceViewBase
		{
			public:
				ID3D11ShaderResourceView *data;

				//constructor
				shaderResourceViewD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderResourceViewD3D()
				{
					//clears the buffer
					clear();
				}
		};
		//shader resource view
		class unorderedAccessViewD3D:public unorderedAccessViewBase
		{
			public:
				ID3D11UnorderedAccessView *data;

				//constructor
				unorderedAccessViewD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~unorderedAccessViewD3D()
				{
					//clears the buffer
					clear();
				}
		};
		

		//class to handle vertex shader data
		class shaderVertexD3D:public shaderVertexBase
		{
			public:
				ID3D11VertexShader *data;

				//constructor
				shaderVertexD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderVertexD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle hull shader data
		class shaderHullD3D:public shaderHullBase
		{
			public:
				ID3D11HullShader *data;

				//constructor
				shaderHullD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderHullD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle domain shader data
		class shaderDomainD3D:public shaderDomainBase
		{
			public:
				ID3D11DomainShader *data;

				//constructor
				shaderDomainD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderDomainD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle geometry shader data
		class shaderGeometryD3D:public shaderGeometryBase
		{
			public:
				ID3D11GeometryShader *data;

				//constructor
				shaderGeometryD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderGeometryD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle pixel shader data
		class shaderPixelD3D:public shaderPixelBase
		{
			public:
				ID3D11PixelShader *data;

				//constructor
				shaderPixelD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderPixelD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class to handle compute shader data
		class shaderComputeD3D:public shaderComputeBase
		{
			public:
				ID3D11ComputeShader *data;

				//constructor
				shaderComputeD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~shaderComputeD3D()
				{
					//clears the buffer
					clear();
				}
		};
		
		//sampler state
		class samplerStateD3D:public samplerStateBase
		{
			public:
				ID3D11SamplerState *data;

				//constructor
				samplerStateD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~samplerStateD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class instance for shader
		class classInstanceD3D:public classInstanceBase
		{
			public:
				ID3D11ClassInstance *data;

				//constructor
				classInstanceD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~classInstanceD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//class for Rasterizer State
		class rasterizerStateD3D:public rasterizerStateBase
		{
			public:
				ID3D11RasterizerState *data;

				//constructor
				rasterizerStateD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~rasterizerStateD3D()
				{
					//clears the buffer
					clear();
				}
		};

		//command list
		class commandListD3D:public commandListBase
		{
			public:
				ID3D11CommandList *data;

				//constructor
				commandListD3D(void)
				{
					data=NULL;
				}

				//clears the buffer
				void clear(void)
				{
					if(data)
					{
						data->Release();
						data=NULL;
					}
				}

				//destructor
				~commandListD3D()
				{
					//clears the buffer
					clear();
				}
		};
	
	private:
		

	public:

		//constructor
		graphicsDX(void);
		
		//initilizes the device
		bool init(deviceBase *gDevice, deviceContextBase *dContext, texture2DBase *renderTarget, texture2DBase *depthStencil,renderTargetViewBase *rTargetView,depthStencilViewBase *dStencilView);

		//closes the device
		bool close(void);

		//resizes the output screen
		bool resize(deviceBase *gDevice, deviceContextBase *dContext, texture2DBase *renderTarget, texture2DBase *depthStencil,renderTargetViewBase *rTargetView,depthStencilViewBase *dStencilView, const unsigned int &_width,const unsigned int &_height);

		//swaps back and front buffer
		inline void swapBuffer(void);

		//sets vsync option
		inline void setVerticalSync(bool option);

		//checks for any error
		inline bool checkError(void);
		
		//destructor
		~graphicsDX();

		//compiles shader for the file
		bool compileShaderFromFile(char* fileName,char *entryPoint, char *shaderModel, blobBase* shData);

		//sets window handler
		void setWindowHandler(const HWND &_hWnd);

		//returns window handle
		inline HWND& getWindowHandler(void){return hWnd;};
};

#endif